/**
 * 
 */
package com.vision.core.cm.db.data;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import net.sf.gilead.pojo.gwt.LightEntity;

/**
 * @author Mark
 *
 */
@Entity
@Table(name = Role.TABLE)
public class Role extends LightEntity implements Serializable {
	
	public static interface FieldSpecs {
		int NAME_MAX = 60;
		int DESC_MAX = 300;
	}

	private static final long serialVersionUID = -4506464588820612531L;
	
	public static final String TABLE = "role";
	public static final String COL_NAME = "name";
	public static final String COL_DESCRIPTION = "description";
	
	public static final String TABLE_ROLE_PERMISSION = "role_permission";
	public static final String COL_ROLE_NAME = "role_name";
	public static final String COL_PERMISSION_NAME = "permission_name";
	
	public static final String ROOT_NAME = "root";
	
	@Id
	@Column(name = COL_NAME, updatable = false, length = FieldSpecs.NAME_MAX)
	private String name;
	
	@Column(name = COL_DESCRIPTION, length = FieldSpecs.DESC_MAX)	
	private String description;
	
	@ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	@JoinTable(name = TABLE_ROLE_PERMISSION
			//, XXX Thanks to Hibernate's stupid "column ordering is not important" mantra, it's become
			// a pain to define your own column names when one of the table has multiple primary keys.
			// In the case below, the columns in inverseJoinColumns will not map to the columns in the
			// "permission" table via column name matching, but rather via column order matching. And since Hibernate's column
			// ordering is arbitrary, the column "permission_app_id" in this join table may not
			// map to the column "permission_app_id" in the "permissions" table.
	
			,joinColumns = 			{ @JoinColumn(name = COL_ROLE_NAME, referencedColumnName = COL_NAME) }
			,inverseJoinColumns = 	{ @JoinColumn(name = COL_PERMISSION_NAME, referencedColumnName = Permission.COL_NAME) }
			)
	private Set<Permission> permissions = new HashSet<Permission>();
	
	@ManyToMany(mappedBy = "roles")
	private Set<User> users = new HashSet<User>();
	
	public Role() {}
	
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Role) {
			Role r = (Role) obj;
			return getName().equals(r.getName());
		}
		return false;
	}
	
	@Override
	public int hashCode() {
		return getName() == null ? super.hashCode() : getName().hashCode();
	}
	
	@Override
	public String toString() {
		return name;
	}

	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Set<Permission> getPermissions() {
		return permissions;
	}

	public void setPermissions(Set<Permission> permissions) {
		this.permissions = permissions;
	}
	
	public void setPermissionsFromArray(Permission[] permissionsToSet) {
		if (this.permissions == null)
			this.permissions = new HashSet<Permission>(permissionsToSet.length);
		for (Permission permission : permissionsToSet)
			this.permissions.add(permission);
	}
	
	public void addPermissions(List<Permission> permissionsToAdd) {
		if (this.permissions == null)
			this.permissions = new HashSet<Permission>();
		this.permissions.addAll(permissionsToAdd);
	}
	
	public void removePermissions(List<Permission> permissionsToRemove) {
		if (this.permissions != null)
			this.permissions.removeAll(permissionsToRemove);
	}
	
	/**
	 * Returns true if the {@link Role} has any of the {@link Permission}s specified.
	 * @param permissionsToCheck
	 * @return
	 */
	public boolean hasAnyPermission(Permission ... permissionsToCheck) {
		if (this.permissions == null) return false;
		for (Permission permission : permissionsToCheck) {
			if (this.permissions.contains(permission))
				return true;
		}
		return false;
	}
	
	/**
	 * Returns true if the {@link Role} has all of the {@link Permission} specified.
	 * @param permissionsToCheck
	 * @return
	 */
	public boolean hasAllPermissions(Permission ... permissionsToCheck) {
		if (this.permissions == null) return false;
		for (Permission permission : permissionsToCheck) {
			if (!this.permissions.contains(permission))
				return false;
		}
		return true;
	}
	
	public Set<User> getUsers() {
		return users;
	}
	
	public void setUsers(Set<User> users) {
		this.users = users;
	}
	
}
